﻿/**             © 2008 Avanade Inc. All Rights Reserved.
 * 
 * Authors:     Joris Valkonet joris.valkonet@avanade.com  Avanade Netherlands
 *              Thanh Luc      thanh.luc@avanade.com       Avanade Netherlands
 *              Mark Beerens   mark.beerens@avanade.com    Avanade Netherlands
 * 
 * Content:     This method paints a scatter plot if both selected attributes are discrete
 *                 
 * */
#region [Using]
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Data;
//using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
#endregion

namespace Avanade.Datamining.SVMPluginViewer
{
    public partial class SVMViewerUserControl : UserControl
    {

        #region [Paint]
        /// <summary>
        /// The main paint function
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void OnDiscretePaint(Object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;

            this.DrawDiscreteScatterPlot(g);

            // Draw the ScatterPlot Axes and Grid
            this.DrawDiscreteScatterPlotAxesGrid(g);

        }
        #endregion

        #region drawBuckets

        /// <summary>
        /// This method draws the bars of the discrete distribution
        /// </summary>
        /// <param name="g"></param>
        private void DrawDiscreteScatterPlot(Graphics g)
        {
            
            try{
                //get the discrete values of both selected attributes
                //if one of the two cannot be found... make it empty
                LinkedList<String> x_Discretes;
                LinkedList<String> y_Discretes;

                discreteValues.TryGetValue(x_name, out x_Discretes);
                discreteValues.TryGetValue(y_name, out y_Discretes);

                if (x_Discretes == null || y_Discretes == null)
                {
                    MessageBox.Show("The attribute " + x_name + " or the attribute " + y_name + " cannot be found.", "Error");
                    return;
                }
   
                //build buckets
                //the buckes are 3 dimensional.. 
                //1st dimension: the discrete values of the x-attribute
                //2nd dimension: the discrete values of the y-attribute
                //3rd dimension: the predict values
                //
                //each example in the selected set is added to a bucket
                //and then the buckets are drawn

                //step 1.. build the structure of the buckets needed
                int [][][] buckets = new int[x_Discretes.Count][][];
                for (int i = 0; i < buckets.Length; i++)
                {
                    buckets[i] = new int[y_Discretes.Count][];
                    int numberOfAtts = classes.Length;;
                    if (showClassifier)
                    {
                        numberOfAtts++;
                    }
                    for (int j = 0; j<buckets[i].Length; j++)
                    {
                        buckets[i][j] = new int[numberOfAtts];
                    }
                }

                if (buckets.Length == 0)
                {
                    return;
                }
                if (buckets[0].Length == 0)
                {
                    return;
                }

                //get the selected data
                DataTable table;
                if (data.Equals("Trainingdata"))
                {
                    table = trainingset;
                }
                else
                {
                    table = testingSet;
                }

                //fill the buckets
                foreach (DataRow myRow in table.Rows)
                {
                    //SortedList
                    String valueX = GetStringValue(myRow, x_name);
                    String valueY = GetStringValue(myRow, y_name);
                    // retrieve the class value
                    String classValue = GetStringValue(myRow, "__class");
                    // retrieve the predict value
                    String predictValue = GetStringValue(myRow, "__predict");

                    //determine the discrete values
                    int x_discreteValue = getPositionOfValue(x_Discretes, valueX);
                    int y_discreteValue = getPositionOfValue(y_Discretes, valueY); 

                    //add the example to a bucket
                    if (showClassifier && (classValue != predictValue))
                    {
                        buckets[x_discreteValue][y_discreteValue][buckets[x_discreteValue][y_discreteValue].Length-1]++;
                    }
                    else
                    {
                        int myclassValue = getPositionOfValue(new LinkedList<String>(classes), classValue); //todo0;
                        if (myclassValue != -1)
                        {
                            buckets[x_discreteValue][y_discreteValue][myclassValue]++;
                        }
                    }

                }

                //determine the maximum examples in a bucket
                int max = 0;
                for (int i = 0; i < buckets.Length; i++)
                {
                    for (int j = 0; j < buckets[i].Length; j++)
                    {
                        for (int z = 0; z < buckets[i][j].Length; z++)
                        {
                            if (buckets[i][j][z] > max)
                            {
                                max = buckets[i][j][z];
                            }
                        }
                    }
                }

                //draw the buckets
                int tmpWidth = (int)canvasWidth / buckets.Length;
                int tmpHeight = (int)canvasHeight / buckets[0].Length;

                for (int i = 0; i < buckets.Length; i++)
                {
                    for (int j = 0; j < buckets[i].Length; j++)
                    {
                        //determine the total number of cases
                        int numberOfDiscreteValues = buckets[i][j].Length;


                        //with of a class
                        int bucketWidth = (int)tmpWidth / numberOfDiscreteValues;

                        for (int z = 0; z < buckets[i][j].Length; z++)
                        {
                            //determine the color
                            Color myColor = Color.Black;
                            if (z == classes.Length)
                            {
                                myColor = misclassifiedColor;
                            }
                            else
                            {
                                ValueColors.TryGetValue(classes[z], out myColor);
                            }
                                                        
                            Pen myPen = new Pen(myColor);
                            //heigh of a class per case
                            int bucketHeight = (int)((double)tmpHeight * ((double)buckets[i][j][z] / (double)max));
                            int bonus = 0; //extra pixel .. looks better
                            if (z == buckets[i][j].Length - 1)
                            {
                                bonus = 1;
                            }
                            //draw the bucket
                            g.FillRectangle(myPen.Brush, marginLeft + tmpWidth * i + bucketWidth * z, marginTop + tmpHeight * (j + 1) - bucketHeight, bucketWidth + bonus, bucketHeight);
                        }

                    }
                }
            }
            catch(Exception ex)
            {
                //Debug.WriteLine(ex);
                MessageBox.Show(ex.Message);//"An exception occured in the function 'DrawScatterPlot'. This program needs to be terminated.");

            }

        }

        /// <summary>
        /// Returns the position of a discrete value
        /// </summary>
        /// <param name="list"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        private int getPositionOfValue(LinkedList<String> list, String value)
        {
            int i = 0;
            foreach (String item in list)
            {
                if (item == value)
                {
                    return i;
                }
                i++;
            }
            return -1;
        }
#endregion

        #region [Draw Axes]

        /// <summary>
        /// Draw the ScatterPlot Axes and Grid
        /// </summary>
        /// <param name="g"></param>
        private void DrawDiscreteScatterPlotAxesGrid(Graphics g)
        {
            // Draw the rectangle that is the scatter plot     
            g.DrawRectangle(new Pen(Color.White), marginLeft, marginTop, canvasWidth, canvasHeight);


            LinkedList<String> x_Discretes;
            LinkedList<String> y_Discretes;

            discreteValues.TryGetValue(x_name, out x_Discretes);
            discreteValues.TryGetValue(y_name, out y_Discretes);


            // Set text font
            Font font = new Font("Arial", 10);

            // distance between writes
            double xscale = canvasWidth / x_Discretes.Count;
            double yscale = canvasHeight / y_Discretes.Count;

            double xhalfway = xscale / 2;
            double yhalfway = yscale / 2;

            int i = 0;
            foreach (String v in x_Discretes)
            {
                String strX = String.Format("{0}", v);
                double xPos = this.marginLeft + xhalfway + xscale * i;
                g.DrawString(strX, font, new SolidBrush(Color.Black), (int)xPos, marginTop + canvasHeight + 10);
                i++;
            }

            i = 0;
            foreach (String v in y_Discretes)
            {
                String strX = String.Format("{0}", v);
                double yPos = this.marginTop + canvasHeight - yhalfway - xscale * i;
                g.DrawString(strX, font, new SolidBrush(Color.Black), marginLeft - 35, (int)yPos);
                i++;
            }

            if (comboBox4.Text == "Yes")
            {
                // Draw horizontal lines              
                for (i = 0; i <= x_Discretes.Count; i++)
                {
                    g.DrawLine(new Pen(Color.Black), new Point(marginLeft, marginTop + i * (int)xscale), new Point(marginLeft + canvasWidth, marginTop + i * (int)xscale));
                }

                // Draw horizontal lines              
                for (i = 0; i <= y_Discretes.Count; i++)
                {
                    g.DrawLine(new Pen(Color.Black), new Point(marginLeft + i * (int)yscale, marginTop), new Point(marginLeft + i * (int)yscale, marginTop + canvasHeight));
                }
            }
        }
        #endregion

    }
}
